/*
 * Copyright the original author or authors.
 * 
 * Licensed under the MOZILLA PUBLIC LICENSE, Version 1.1 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.mozilla.org/MPL/MPL-1.1.html
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.kairos.commands 
{ 
	
	import com.bourre.commands.ASyncCommandListener;
	import com.bourre.commands.Command;
	import com.bourre.log.PixlibDebug;
	import com.bourre.log.PixlibStringifier;
	import com.kairos.engine.RTObject;
	
	import flash.events.Event;
	
	/**
	 * <code>IntervalRT</code> enables you to call a command after a specific
	 * time.
	 * 
	 * <p><code>IntervalRT</code> object work as a call of the <code>setInterval</code> 
	 * method but on commands and can be paused, restart, and synchronized with the
	 * whole application run dint <code>RTBeacon</code>.</p>  
	 * 
	 * <p>Number of iterations of an <code>IntervalRT</code> object can be controlled
	 * at creation or during the interval running. See the <code>setCount</code> method
	 * for details about how iterations limit works.</p>
	 * 
	 * <p><code>IntervalRT</code> implements the <code>ASyncCommand</code> interface, so you could
	 * use it with a <code>StepCommandManager</code>, or in your <code>FrontController</code>.</p>
	 * 
	 * @author	Cédric Néhémie
	 * @example	Creating an interval to call a delegate
	 * command after 1000 milliseconds.
	 * <listing>import com.bourre.commands.Delegate;
	 * import com.kairos.commands.IntervalRT;
	 * 
	 * var myCommand : Delegate = new Delegate ( this, someMethod, "someParam" );
	 * var myInterval : IntervalRT = new IntervalRT ( myCommand, 1000 );
	 * 
	 * myInterval.start (); 
	 * </listing>
	 */	
	public class IntervalRT extends RTObject
	{
		protected var _o : Command;
		protected var _c : Number;
		protected var _d : Number;
		protected var _n : Number;
		protected var _e : Event;
		protected var _i : Number;
		
		/**
		 * Create a new <code>IntervalRT</code> object.
		 * 
		 * <p>You have to call the <code>execute</code>, <code>start<code>
		 * or <code>play</code> method to start the Interval run.</p>
		 * 
		 * <p>Interval could run during a specified number of times by using
		 * the third argument. If not defined the count is set to 0, and the
		 * interval will run indefitely.</p>
		 * 
		 * <p>You can specify an additionnal event arguments to pass to the
		 * command when executed.</p>
		 *	
		 * @param	o	<code>Command</code> to execute.
		 * @param	d	<code>Number</code> of milliseconds between each interval.
		 * @param	c	<code>Number</code> of iteration to perform.
		 * @param	e	<code>Event</code> to pass to the command.
		 * @example	
		 * <listing>var myInterval : IntervalRT = new IntervalRT ( this, myFunction, 1000 );</listing>
		 * <listing>var myInterval : IntervalRT = new IntervalRT ( this, myFunction, 1000, "someParam" );</listing>
		 */	
		public function IntervalRT ( o : Command, d : uint = 0, c : uint = 0, e : Event = null)
		{
			setCommand( o );
			setDelay( d );
			setCount( c );
			setArgument( e );
			reset();
		}
		
		/**
		 * Method called by the <code>RTBeacon</code> object.
		 * 
		 * <p>For each step of the run the current step is added
		 * to the time clock. The current step is get from the
		 * <code>RTBeacon</code>.</p>
		 * 
		 * <p>The command receive a <code>RTEvent</code> when called
		 * by the interval. That is the event received in the constructor
		 * which is passed to the command.</p>
		 */
		public override function onTick ( e : Event = null ) : void
		{		
			_n += getLocalStep( e );
			
			if( _n >= _d )
			{
				_o.execute( _e );
				_n -= _d;
				if( _c && ++_i >= _c )
				{
					stop();
					fireCommandEndEvent();
					reset();
				}
			}
		}
		
		/**
		 * Returns the current interval time in milliseconds.
		 * 
		 * @return <code>Number</code> of milliseconds in the iteration
		 */
		public function getTime() : Number
		{
			return _n;
		}
			
		/**
		 * Moves the internal timer to 0, if the <code>IntervalRT</code> is running
		 * it continue to proceed the run.
		 * 
		 * <p>If an interval count have been specified the counter is also reset at
		 * the same time.</p>
		 */
		override public function reset () : void
		{
			_n = 0;
			_i = 0;
		}
		
		/**
		 * Defines the command the interval has to call.
		 * 
		 * <p>When a command is define during the interval run, the next 
		 * interval which automatically call the new <code>Command</code>.</p>
		 * 
		 * @param o	<code>Command</code> object to call at the end of interval.
		 */
		public function setCommand ( o : Command ) : void
		{
			_o = o;
		}
		
		/**
		 * Returns the current command which this interval will call.
		 * 
		 * @return Command called by the interval.
		 */
		public function getCommand () : Command 
		{
			return _o;
		}
		
		
		/**
		 * Defines the number of iterations the interval has to realize
		 * during its run.
		 * 
		 * <p>When the interval is running, if the specified value is lower
		 * than the current iteration, the interval will stop at the next
		 * interval.</p>
		 * 
		 * <p>If set to 0, the count is deactivate.</p>
		 * 
		 * @param 	c	Number of iteration to perform.
		 */
		public function setCount ( c : uint ) : void
		{
			_c = c;
		}
		
		/**
		 * Returns the current count limit for this object.
		 * 
		 * @return the current count limit for this object.
		 */
		public function getCount() : uint
		{
			return _c;
		}
				
		/**
		 * Returns the current iteration of this object.
		 * 
		 * @return the current iteration of this object.
		 */
		public function getIteration () : uint
		{
			return _i;
		}
		
		/**
		 * Defines the delay between each call.
		 * 
		 * <p>When running, if the specified delay is
		 * lower than the current internal timer, the
		 * command will be called at the next step.</p> 
		 * 
		 * @param	d	Number of milliseconds between calls
		 */
		public function setDelay ( d : uint ) : void
		{
			_d = d;
		}
		
		/**
		 * Returns the current delay between calls for this object.
		 * 
		 * @return the current delay between calls for this object
		 */
		public function getDelay () : uint
		{
			return _d;
		}
		
		/**
		 * Defines an event object which is passed to the command
		 * by this interval.
		 * 
		 * @param	e	Event to pass to the command.
		 */
		public function setArgument( e : Event = null ) : void
		{
			_e = e;
		}
		
		/**
		 * Returns the current event object to pass to the command.
		 * 
		 * @return the current event object to pass to the command.
		 */
		public function getArgument() : Event
		{
			return _e;
		}
		
		/**
		 * Adds <code>ASyncCommandListener</code> as listener for the
		 * <code>onCommandEnd</code> event of the current object.
		 * 
		 * <p>A warning is send to the debugger if you are trying
		 * to register to an interval which have no limit
		 * for execution.</p>
		 * 
		 * @param	listener <code>ASyncCommandListener</code> object to add
		 * 					 as listener for the <code>onCommandEnd</code> event
		 * 					 of the current object.
		 * @param	rest	 Any optional arguments in order to create proxy method
		 * 					 listeners.
		 * @return	<code>true</code> if the object have been succesfully registered
		 * 			as listener for this object, either <code>false</code>.
		 */
		public override function addASyncCommandListener( listener : ASyncCommandListener, ... rest ) : Boolean
		{
			if( _c == 0 ) 
				PixlibDebug.WARN( listener + " have registered to an object which will not fire the onCommandEnd event : " + this ); 
			
			return super.addASyncCommandListener.apply( null, [ listener ].concat( rest ) );
		}
		
		/**
		 * Returns the string representation of the object.
		 * 
		 * @return the string representation of the object.
		 */	
		public override function toString() : String
		{
			return PixlibStringifier.stringify( this );
		}
	}
}